/**
 * 钩子系统核心模块 (Hooks System Core Module)
 *
 * 提供类似React Hooks的状态和生命周期管理API
 * 包含useState、useEffect、useRef等基础钩子及其实现
 *
 * 本模块实现了一个轻量级的类React钩子系统，允许函数组件维护状态和副作用。
 * 支持所有常见的React钩子并保持相同的使用模式，便于开发者快速上手。
 * 内部实现了批量更新优化，避免重复渲染提高性能。
 */

import { addResizeListener, currentScreenSize } from "./renderer";
import { createElement } from "./createElement";

// Ref类型定义
export namespace ReactTypes {
  /**
   * 引用对象类型（RefObject）
   * 用于访问DOM节点或其他持久化数据存储
   * 与React的RefObject概念一致，提供对实例的稳定引用
   * 允许组件在重新渲染之间保持对特定值的引用
   */
  export interface RefObject<T> {
    current: T | null;
  }
}

// 全局状态管理变量
let currentComponent: any = null; // 当前正在执行的组件实例引用
let hookIndex = 0; // 当前钩子在组件钩子数组中的索引位置

// 组件钩子存储系统 - 每个组件实例对应一个钩子数组
// 使用Map数据结构保存组件实例与其钩子数组之间的映射关系
export const componentHooks = new Map<any, any[]>();

// 更新追踪系统 - 防止一个事件循环中重复触发渲染，提高性能
const pendingUpdates = new Map<any, number>();

// 批处理更新系统 - 合并多个状态更新为单次渲染
// batchedUpdates保存需要更新的组件及其触发更新的钩子索引
let batchedUpdates = new Map<any, Set<number>>();
let isBatchingUpdates = false; // 是否正在收集批量更新的标志
let updateTimeout: number | null = null; // 处理批量更新的定时器ID

// 上下文系统 - 实现跨组件的状态共享
// contextMap保存上下文ID与当前值的映射
export const contextMap = new Map<Symbol, any>();
// contextSubscriptions保存上下文ID与订阅该上下文的组件集合的映射
export const contextSubscriptions = new Map<Symbol, Set<any>>();

/**
 * 处理批量更新队列
 * 确保同一事件循环中的多个状态更新只触发一次渲染
 *
 * 该函数被调度系统在一个微任务中调用，处理所有累积的组件更新
 * 通过批处理机制大幅减少重新渲染次数，提高应用性能
 */
function processBatchedUpdates() {
  // 清除更新定时器
  if (updateTimeout !== null) {
    clearTimeout(updateTimeout);
    updateTimeout = null;
  }

  // 复制待更新组件集合并重置更新队列
  const componentsToUpdate = new Set([...batchedUpdates.keys()]);
  batchedUpdates = new Map();
  isBatchingUpdates = false;

  // 依次更新每个组件
  for (const component of componentsToUpdate) {
    if (!component || typeof component.update !== "function") continue;

    // 重置组件更新状态标志，防止更新过程中的递归渲染
    component.isUpdating = false;
    component._updateThrottled = false;

    // 执行组件更新（重新渲染）
    try {
      component.update();
    } catch (err) {
      console.error("组件更新错误:", err);
    }
  }
}

/**
 * 安排批量更新
 *
 * 将组件更新请求添加到批处理队列，并安排一个未来的微任务处理所有更新
 * 这是批处理系统的核心函数，确保多个状态更新只触发一次渲染
 *
 * @param component 要更新的组件实例
 * @param hookIdx 触发更新的钩子索引，用于追踪是哪个状态变化引起的更新
 */
export function scheduleBatchedUpdate(component: any, hookIdx: number) {
  if (!component) return;

  // 将组件及对应的钩子索引添加到批处理队列
  if (!batchedUpdates.has(component)) {
    batchedUpdates.set(component, new Set());
  }
  batchedUpdates.get(component)!.add(hookIdx);

  // 如果当前不在批处理中，安排一个新的批处理任务
  if (!isBatchingUpdates) {
    isBatchingUpdates = true;
    // 使用setTimeout模拟React的调度系统，在当前事件循环结束后处理更新
    updateTimeout = setTimeout(processBatchedUpdates, 0) as unknown as number;

    // 备份机制，防止setTimeout失败导致更新丢失
    if (!updateTimeout) {
      processBatchedUpdates();
    }
  }
}

/**
 * 设置当前活动组件
 *
 * 在组件函数执行前调用，建立当前执行上下文
 * 使钩子函数能够识别自己被哪个组件调用，实现钩子和组件的关联
 *
 * @param component 当前正在渲染的组件实例
 */
export function setCurrentComponent(component: any) {
  currentComponent = component; // 设置全局当前组件引用
  hookIndex = 0; // 重置钩子索引，确保按顺序访问组件的钩子数组
}

/**
 * useState钩子 - 管理组件状态
 *
 * 允许函数组件在多次渲染之间保持状态，是整个钩子系统最核心的API
 * 返回当前状态值和更新函数的数组，与React的useState用法完全一致
 * 状态更新会触发组件重新渲染，但会自动合并在一个更新周期内执行
 *
 * 工作原理：
 * 1. 每个组件实例维护一个钩子数组，保存所有状态
 * 2. 通过调用顺序确定每个useState对应的状态位置
 * 3. 状态更新会引发组件重新渲染，但多个更新会被批处理
 *
 * @param initialState 初始状态值或返回初始状态的函数
 * @returns [状态值, 状态更新函数] 类型的数组
 */
export function useState<T>(
  initialState: T
): [T, (newState: T | ((prevState: T) => T)) => void] {
  // 确保钩子在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useState必须在组件函数内部调用");
  }

  // 保存当前组件引用，避免闭包问题导致更新错误的组件
  const componentInstance = currentComponent;

  // 获取或创建组件的钩子数组（首次渲染时创建）
  if (!componentHooks.has(componentInstance)) {
    componentHooks.set(componentInstance, []);
  }

  const hooks = componentHooks.get(componentInstance)!;
  const index = hookIndex++; // 获取当前钩子索引并递增，确保钩子调用顺序

  // 初始化钩子状态(仅首次渲染时执行)
  if (index >= hooks.length) {
    hooks[index] = {
      value:
        // 支持惰性初始化：如果initialState是函数则调用它
        typeof initialState === "function"
          ? (initialState as Function)()
          : initialState,
    };
  }

  const hook = hooks[index];

  // 状态更新函数 - 闭包保存了组件实例和钩子索引信息
  const setState = (newState: T | ((prevState: T) => T)) => {
    // 计算新状态 - 支持函数式更新，传入前一个状态计算新状态
    const nextState =
      typeof newState === "function"
        ? (newState as (prevState: T) => T)(hook.value)
        : newState;

    // 状态未变化时不触发更新（引用相等性检查）
    if (Object.is(nextState, hook.value)) return;

    // 更新钩子中存储的状态值
    hook.value = nextState;
    if (!componentInstance) return;

    // 安排组件更新，传入触发更新的钩子索引
    scheduleBatchedUpdate(componentInstance, index);
  };

  // 返回[当前状态, 更新函数]数组
  return [hook.value, setState];
}

/**
 * 依赖数组比较函数
 *
 * 使用严格相等比较依赖数组的每个元素，判断是否需要重新执行副作用
 * 这是实现useEffect、useMemo等带依赖的钩子的核心工具函数
 *
 * @param a 前一个依赖数组
 * @param b 新的依赖数组
 * @returns 如果数组相等返回true（不需要重新执行），否则返回false
 */
function areArraysEqual(a?: any[], b?: any[]): boolean {
  // 处理null或undefined情况
  if (!a || !b) return !a && !b;
  // 长度不同则必然不相等
  if (a.length !== b.length) return false;

  // 使用Object.is进行严格相等比较，比===更严格（能区分NaN和-0/+0）
  for (let i = 0; i < a.length; i++) {
    if (!Object.is(a[i], b[i])) return false;
  }

  return true;
}

/**
 * useEffect钩子 - 处理组件的副作用
 *
 * 允许在函数组件中执行副作用操作，如数据获取、订阅事件、手动DOM操作等
 * 接收一个副作用函数和可选的依赖数组，仅当依赖项变化时才会重新执行副作用
 * 副作用函数在渲染后异步执行，不会阻塞浏览器绘制
 *
 * 工作原理：
 * 1. 每次渲染时检查依赖数组是否变化
 * 2. 如有需要，清理上一次副作用并在渲染后异步执行新的副作用
 * 3. 支持返回清理函数，在组件卸载或依赖变化时执行清理
 *
 * @param callback 副作用函数，可返回清理函数用于清理资源
 * @param deps 依赖项数组，仅当这些值变化时才会重新执行副作用
 */
export function useEffect(callback: () => void | (() => void), deps?: any[]) {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useEffect必须在组件函数内部调用");
  }

  // 保存组件实例引用并获取/初始化钩子数组
  const componentInstance = currentComponent;
  if (!componentHooks.has(componentInstance)) {
    componentHooks.set(componentInstance, []);
  }

  const hooks = componentHooks.get(componentInstance)!;
  const index = hookIndex++; // 当前effect在钩子数组中的位置

  // 检查依赖项是否变化，决定是否需要重新执行
  let depsChanged = true;
  if (index < hooks.length && deps && hooks[index].deps) {
    depsChanged = !areArraysEqual(deps, hooks[index].deps);
  }

  // 依赖项变化或首次渲染时需要设置新的副作用
  if (index >= hooks.length || depsChanged) {
    // 执行之前的清理函数（如果存在）
    if (index < hooks.length && typeof hooks[index].cleanup === "function") {
      try {
        hooks[index].cleanup();
      } catch (err) {
        console.error("Effect清理函数错误:", err);
      }
    }

    // 安排在渲染后异步运行副作用（模拟React的提交阶段）
    const runEffect = () => {
      // 组件已卸载则不执行
      if (!componentHooks.has(componentInstance)) return;

      try {
        // 确保组件已成功渲染
        const rootNode = componentInstance.result;
        if (rootNode) {
          // 执行副作用并获取可能返回的清理函数
          const cleanup = callback();
          // 更新钩子状态，保存新的依赖和清理函数
          const currentHooks = componentHooks.get(componentInstance);
          if (currentHooks && index < currentHooks.length) {
            currentHooks[index] = { deps, cleanup };
          } else if (currentHooks) {
            currentHooks.push({ deps, cleanup });
          }
        }
      } catch (err) {
        console.error("执行useEffect回调时出错:", err);
      }
    };

    // 使用setTimeout确保副作用在渲染后异步执行
    setTimeout(runEffect, 0);

    // 如果钩子不存在（首次渲染），则初始化
    if (index >= hooks.length) {
      hooks[index] = { deps };
    }
  }
}

/**
 * useRef钩子 - 创建可变的引用对象
 *
 * 返回一个可变的引用对象，其.current属性被初始化为传入的参数
 * 返回的对象在组件的整个生命周期内保持不变，可用于:
 * 1. 引用DOM节点或组件实例
 * 2. 存储任意可变值，类似实例属性
 * 3. 保存那些不需要触发重新渲染的数据
 *
 * 与useState不同，修改ref.current不会触发组件重新渲染
 *
 * @param initialValue 引用对象的初始值
 * @returns 包含current属性的可变引用对象
 */
export function useRef<T>(initialValue: T): { current: T } {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useRef只能在函数组件内部使用");
  }

  // 获取当前组件的钩子数组
  if (!componentHooks.has(currentComponent)) {
    componentHooks.set(currentComponent, []);
  }

  const hooks = componentHooks.get(currentComponent)!;
  const index = hookIndex++;

  // 如果还未初始化（首次渲染），则创建新的ref对象
  if (index >= hooks.length) {
    hooks[index] = { current: initialValue };
  }

  // 返回ref对象，在整个组件生命周期内保持稳定引用
  return hooks[index];
}

/**
 * useMemo钩子 - 缓存计算结果
 *
 * 返回一个记忆化的值，只有在依赖项变化时才重新计算
 * 用于优化性能，避免在每次渲染时都进行昂贵的计算
 *
 * 工作原理:
 * 1. 首次调用时执行计算函数并缓存结果
 * 2. 后续渲染时比较依赖数组，仅在依赖变化时重新计算
 * 3. 否则返回缓存的值，避免重复计算
 *
 * @param factory 创建记忆化值的工厂函数
 * @param deps 依赖项数组，仅当这些值变化时才重新执行factory
 * @returns 记忆化的计算结果
 */
export function useMemo<T>(factory: () => T, deps?: any[]): T {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useMemo只能在函数组件内部使用");
  }

  // 获取或初始化钩子数组
  if (!componentHooks.has(currentComponent)) {
    componentHooks.set(currentComponent, []);
  }

  const hooks = componentHooks.get(currentComponent)!;
  const index = hookIndex++;

  // 检查依赖项是否变化
  let depsChanged = true;
  if (index < hooks.length && deps && hooks[index].deps) {
    depsChanged = !areArraysEqual(deps, hooks[index].deps);
  }

  // 如果是首次渲染或依赖项发生变化，重新计算值
  if (index >= hooks.length || depsChanged) {
    try {
      // 调用工厂函数获取新的计算结果
      const value = factory();

      // 存储新值和依赖项
      if (index >= hooks.length) {
        // 首次渲染时初始化钩子状态
        hooks[index] = { value, deps };
      } else {
        // 依赖变化时更新现有钩子状态
        hooks[index].value = value;
        hooks[index].deps = deps;
      }

      // 与useEffect不同，useMemo在依赖变化时会触发组件更新
      // 这确保了所有依赖该记忆值的派生计算也能更新
      if (depsChanged && index < hooks.length) {
        scheduleBatchedUpdate(currentComponent, -1);
      }

      return value;
    } catch (error) {
      // 错误处理，确保失败时不会中断渲染
      console.error("执行useMemo计算函数时出错:", error);
      if (index >= hooks.length) {
        hooks[index] = { value: undefined, deps };
      }
      return hooks[index].value;
    }
  }

  // 如果依赖没变化，直接返回缓存的值
  return hooks[index].value;
}

/**
 * useCallback钩子 - 记忆化回调函数
 *
 * 返回一个记忆化的回调函数，仅在依赖项变化时才创建新的函数引用
 * 主要用于性能优化，防止因函数引用变化导致子组件不必要的重新渲染
 *
 * 实现原理:
 * 本质上是useMemo的特殊用例，专门用于记忆函数引用
 * 与直接在组件中定义函数的区别是，useCallback返回的函数只在依赖变化时才更新引用
 *
 * @param callback 需要记忆化的回调函数
 * @param deps 依赖项数组，仅当这些值变化时才返回新的函数引用
 * @returns 记忆化的回调函数，在依赖不变时保持稳定引用
 */
export function useCallback<T extends Function>(callback: T, deps: any[]): T {
  // 使用useMemo实现，返回记忆化的回调函数本身
  return useMemo(() => callback, deps);
}

/**
 * useScreenSize钩子 - 响应式获取并监听屏幕尺寸
 *
 * 返回当前屏幕尺寸信息，并在尺寸变化时自动更新
 * 用于构建响应式UI，让组件能够根据屏幕尺寸调整布局和行为
 *
 * 工作原理:
 * 1. 初始化时返回当前屏幕尺寸
 * 2. 使用useEffect设置屏幕调整监听器
 * 3. 在尺寸变化时更新状态，触发组件重新渲染
 *
 * @returns 包含屏幕宽高的对象，在尺寸变化时自动更新
 */
export function useScreenSize(): ScreenResizeEvent {
  // 使用useState保存屏幕尺寸状态
  const [size, setSize] = useState<ScreenResizeEvent>({ ...currentScreenSize });

  // 使用useEffect设置屏幕尺寸变化监听
  useEffect(() => {
    // 添加尺寸变化监听器，在尺寸变化时更新状态
    const removeListener = addResizeListener((newSize) => {
      setSize({ ...newSize });
    });

    // 返回清理函数，在组件卸载时移除监听器
    return removeListener;
  }, []); // 空依赖数组确保监听器只设置一次

  // 返回当前屏幕尺寸
  return {
    screenWidth: size.screenWidth,
    screenHeight: size.screenHeight,
  };
}

/**
 * useContext钩子 - 订阅并获取上下文值
 *
 * 允许组件订阅上下文变化并获取当前上下文值
 * 当上下文值变化时，所有使用该上下文的组件都会重新渲染
 *
 * 工作原理:
 * 1. 获取上下文当前值
 * 2. 将当前组件添加到上下文的订阅者列表
 * 3. 在组件卸载时清理订阅
 *
 * @param context 由createContext创建的上下文对象
 * @returns 上下文的当前值
 */
export function useContext<T>(context: { _contextId: Symbol }) {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useContext只能在函数组件内部使用");
  }

  // 获取上下文ID
  const { _contextId } = context;

  // 初始化钩子存储
  if (!componentHooks.has(currentComponent)) {
    componentHooks.set(currentComponent, []);
  }

  const hooks = componentHooks.get(currentComponent)!;
  const index = hookIndex++;

  // 从全局上下文映射中获取当前上下文值
  const contextValue = contextMap.get(_contextId);

  // 将当前组件添加到此上下文的订阅列表中
  const subscribers = contextSubscriptions.get(_contextId);
  if (subscribers && !subscribers.has(currentComponent)) {
    // 添加到订阅者集合
    subscribers.add(currentComponent);

    // 使用useEffect在组件卸载时清理订阅
    useEffect(() => {
      return () => {
        // 清理函数：组件卸载时从订阅列表中移除
        const currentSubscribers = contextSubscriptions.get(_contextId);
        if (currentSubscribers) {
          currentSubscribers.delete(currentComponent);
        }
      };
    }, []);
  }

  // 如果钩子不存在，则初始化
  if (index >= hooks.length) {
    hooks[index] = { contextId: _contextId };
  }

  // 返回上下文当前值
  return contextValue;
}

/**
 * useReducer钩子 - 基于reducer的状态管理
 *
 * 一个更强大的useState替代方案，用于处理复杂的状态逻辑
 * 基于Redux模式，通过分发动作(actions)和reducer函数更新状态
 *
 * 工作原理:
 * 1. 接收reducer函数、初始状态和可选的初始化函数
 * 2. 返回当前状态和dispatch函数
 * 3. 通过dispatch发送动作，reducer计算新状态并触发重新渲染
 *
 * @param reducer 状态计算函数，接收当前状态和动作，返回新状态
 * @param initialState 初始状态
 * @param init 可选的状态初始化函数，用于惰性初始化
 * @returns [当前状态, dispatch函数]数组
 */
export function useReducer<S, A>(
  reducer: (state: S, action: A) => S,
  initialState: S,
  init?: (arg: S) => S
): [S, (action: A) => void] {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useReducer只能在函数组件内部使用");
  }

  // 保存当前组件引用
  const componentInstance = currentComponent;

  // 获取或初始化钩子数组
  if (!componentHooks.has(componentInstance)) {
    componentHooks.set(componentInstance, []);
  }

  const hooks = componentHooks.get(componentInstance)!;
  const index = hookIndex++;

  // 首次渲染时初始化状态
  if (index >= hooks.length) {
    // 支持通过init函数惰性初始化状态
    const initialValue = init ? init(initialState) : initialState;
    hooks[index] = { value: initialValue };
  }

  const hook = hooks[index];

  // 创建dispatch函数 - 用于发送动作更新状态
  const dispatch = (action: A) => {
    // 调用reducer函数计算新状态
    const nextState = reducer(hook.value, action);

    // 如果状态未变化，不触发更新
    if (Object.is(nextState, hook.value)) return;

    // 更新保存的状态值
    hook.value = nextState;
    if (!componentInstance) return;

    // 安排组件更新
    scheduleBatchedUpdate(componentInstance, index);
  };

  // 返回[当前状态, dispatch函数]数组
  return [hook.value, dispatch];
}

/**
 * useLayoutEffect钩子 - 同步执行的副作用
 *
 * 功能与useEffect相似，但在所有DOM变更后同步执行
 * 用于需要在浏览器绘制前执行的DOM测量和修改操作
 *
 * 工作原理:
 * 1. 与useEffect基本相同，但副作用函数同步执行而非异步调度
 * 2. 在DOM更新后、浏览器绘制前执行，可能阻塞视觉更新
 * 3. 适用于需要立即DOM测量或避免闪烁的场景
 *
 * 注意: 尽量使用useEffect以避免性能问题，仅在必要时使用useLayoutEffect
 *
 * @param callback 副作用函数，可返回清理函数
 * @param deps 依赖项数组，只有依赖变化时才重新执行副作用
 */
export function useLayoutEffect(
  callback: () => void | (() => void),
  deps?: any[]
) {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useLayoutEffect只能在函数组件内部使用");
  }

  // 保存组件实例引用
  const componentInstance = currentComponent;

  // 获取或初始化钩子数组
  if (!componentHooks.has(componentInstance)) {
    componentHooks.set(componentInstance, []);
  }

  const hooks = componentHooks.get(componentInstance)!;
  const index = hookIndex++;

  // 检查依赖项是否变化
  let depsChanged = true;
  if (index < hooks.length && deps && hooks[index].deps) {
    depsChanged = !areArraysEqual(deps, hooks[index].deps);
  }

  // 依赖变化或首次渲染时执行
  if (index >= hooks.length || depsChanged) {
    // 清理之前的副作用（如果存在）
    if (index < hooks.length && typeof hooks[index].cleanup === "function") {
      try {
        hooks[index].cleanup();
      } catch (err) {
        console.error("执行useLayoutEffect清理函数时出错:", err);
      }
    }

    // 定义副作用执行函数 - 与useEffect不同，这里是同步执行
    const runEffect = () => {
      // 检查组件是否仍然存在
      if (!componentHooks.has(componentInstance)) return;

      try {
        // 确保组件已渲染
        const rootNode = componentInstance.result;
        if (rootNode) {
          // 执行副作用并获取清理函数
          const cleanup = callback();
          // 更新钩子状态
          const currentHooks = componentHooks.get(componentInstance);
          if (currentHooks && index < currentHooks.length) {
            currentHooks[index] = { deps, cleanup };
          } else if (currentHooks) {
            currentHooks.push({ deps, cleanup });
          }
        }
      } catch (err) {
        console.error("执行useLayoutEffect回调时出错:", err);
      }
    };

    // 关键差异: 与useEffect不同，这里同步执行副作用，不使用setTimeout
    runEffect();

    // 首次渲染时初始化钩子
    if (index >= hooks.length) {
      hooks[index] = { deps };
    }
  }
}

/**
 * useDeferredValue钩子 - 创建一个延迟响应的值的副本
 *
 * 返回输入值的延迟版本，用于推迟非关键UI更新，优化性能
 * 适用于处理频繁变化的大型数据集，允许UI保持响应性
 *
 * 工作原理:
 * 1. 接收一个值和可选的延迟时间
 * 2. 当输入值变化时，不立即更新延迟值
 * 3. 等待指定时间后才更新延迟值，避免频繁更新导致的性能问题
 * 4. 组件可以基于最新值立即渲染关键UI部分，同时使用延迟值渲染非关键部分
 *
 * @param value 需要延迟响应的输入值
 * @param timeoutMs 延迟更新的毫秒数，默认200ms
 * @returns 输入值的延迟版本
 */
export function useDeferredValue<T>(value: T, timeoutMs: number = 200): T {
  // 用于存储定时器ID的ref
  const timeoutRef = useRef<number | null>(null);
  // 存储延迟值的状态
  const [deferredValue, setDeferredValue] = useState<T>(value);

  // 监听输入值变化，延迟更新延迟值
  useEffect(() => {
    // 清除任何现有的定时器，避免多次触发
    if (timeoutRef.current !== null) {
      clearTimeout(timeoutRef.current);
    }

    // 仅当值确实发生变化时才安排更新
    if (!Object.is(value, deferredValue)) {
      // 设置延迟定时器，指定时间后更新延迟值
      timeoutRef.current = setTimeout(() => {
        setDeferredValue(value);
        timeoutRef.current = null;
      }, timeoutMs) as unknown as number;
    }

    // 组件卸载或值再次变化时清理
    return () => {
      if (timeoutRef.current !== null) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [value, timeoutMs]);

  // 返回延迟响应的值
  return deferredValue;
}

/**
 * useTransition钩子 - 标记状态更新为低优先级，提供加载指示器
 *
 * 允许组件将状态更新标记为非紧急的"过渡更新"，同时提供一个加载指示器
 * 用于改善用户体验，区分立即响应的UI和可以延迟更新的部分
 *
 * 工作原理:
 * 1. 返回一个isPending标志和startTransition函数
 * 2. 调用startTransition包装状态更新，标记为低优先级
 * 3. 更新期间isPending为true，可用于显示加载状态
 * 4. 使用较长超时模拟优先级调度
 *
 * @returns [isPending, startTransition] 元组
 *   - isPending: 是否有过渡更新正在进行
 *   - startTransition: 包装状态更新的函数，将其标记为过渡更新
 */
export function useTransition(): [boolean, (callback: () => void) => void] {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useTransition只能在函数组件内部使用");
  }

  // 跟踪是否有过渡正在进行的状态
  const [isPending, setIsPending] = useState<boolean>(false);
  // 存储过渡定时器ID的ref
  const transitionTimeoutRef = useRef<number | null>(null);

  // 创建startTransition函数 - 用于包装低优先级状态更新
  const startTransition = (callback: () => void) => {
    // 如果已经有一个过渡在进行中，先清除它
    if (transitionTimeoutRef.current !== null) {
      clearTimeout(transitionTimeoutRef.current);
    }

    // 设置为待处理状态，组件可据此显示加载指示器
    setIsPending(true);

    // 使用较长的延迟来模拟低优先级更新
    // 实际React实现中，这会利用调度器进行并发渲染
    transitionTimeoutRef.current = setTimeout(() => {
      try {
        // 执行状态更新回调
        callback();
      } catch (error) {
        console.error("执行transition回调时出错:", error);
      } finally {
        // 无论成功与否，都需要重置待处理状态
        setIsPending(false);
        transitionTimeoutRef.current = null;
      }
    }, 100) as unknown as number;
  };

  // 确保在组件卸载时清除任何挂起的过渡
  useEffect(() => {
    return () => {
      if (transitionTimeoutRef.current !== null) {
        clearTimeout(transitionTimeoutRef.current);
      }
    };
  }, []);

  // 返回[isPending标志, startTransition函数]
  return [isPending, startTransition];
}

/**
 * useImperativeHandle 钩子 - 自定义暴露给父组件的实例值
 *
 * 配合forwardRef使用，允许子组件向父组件暴露自定义实例方法或属性
 * 可以选择性地暴露功能，限制父组件对子组件内部的访问
 *
 * 工作原理:
 * 1. 接收父组件传递的ref和一个创建句柄函数
 * 2. 自定义创建要暴露的实例值，而不是直接暴露DOM节点
 * 3. 仅当依赖变化时才重新创建实例值，优化性能
 *
 * @param ref 由父组件通过forwardRef传入的ref对象
 * @param createHandle 创建要暴露给父组件的实例值的函数
 * @param deps 依赖数组，仅当依赖变化时才重新创建实例值
 */
export function useImperativeHandle<T>(
  ref: { current: T | null } | null,
  createHandle: () => T,
  deps: any[] = []
): void {
  // 确保在组件函数内部调用
  if (!currentComponent) {
    throw new Error("useImperativeHandle只能在函数组件内部使用");
  }

  // 处理ref为null的情况
  if (!ref) return;

  // 初始化或获取当前组件的钩子数组
  if (!componentHooks.has(currentComponent)) {
    componentHooks.set(currentComponent, []);
  }

  const hooks = componentHooks.get(currentComponent)!;
  const index = hookIndex++;

  // 检查依赖项是否变化
  let depsChanged = true;
  if (index < hooks.length && hooks[index].deps) {
    depsChanged = !areArraysEqual(deps, hooks[index].deps);
  }

  // 仅当依赖变化或首次渲染时创建新的实例值
  if (index >= hooks.length || depsChanged) {
    try {
      // 调用创建函数获取自定义实例值
      const handle = createHandle();

      // 更新ref.current，使父组件能够访问这些值
      ref.current = handle;

      // 存储或更新钩子状态
      if (index >= hooks.length) {
        // 首次渲染，初始化钩子
        hooks[index] = { deps, handle };
      } else {
        // 依赖变化，更新现有钩子
        hooks[index].deps = deps;
        hooks[index].handle = handle;
      }
    } catch (error) {
      console.error("执行useImperativeHandle回调时出错:", error);
      if (index >= hooks.length) {
        hooks[index] = { deps };
      }
    }
  } else if (hooks[index].handle) {
    // 依赖未变化但有缓存的实例值，确保ref.current指向它
    ref.current = hooks[index].handle;
  }
}

/**
 * useRemoteChannel 钩子 - 处理客户端与服务端通信
 *
 * 提供一个简洁的接口用于监听服务端事件和发送事件到服务端，
 * 自动处理组件卸载时取消监听，避免内存泄漏。
 *
 * 工作原理:
 * 1. 提供发送事件到服务端的函数
 * 2. 设置监听服务端事件的回调，并在组件卸载时自动清理
 * 3. 可选地过滤特定类型的事件
 *
 * @template TSend 发送到服务端的事件类型
 * @template TReceive 从服务端接收的事件类型
 *
 * @param options 配置选项对象
 * @param options.eventFilter 事件过滤器函数，决定哪些事件需要被处理
 *        - 返回true表示处理该事件，返回false表示忽略
 *        - 可用于根据事件类型、内容或其他条件筛选事件
 *        - 例如: `event => event.type === "notification"` 只处理通知类型的事件
 *
 * @param options.onReceive 事件接收回调函数，处理从服务端接收的事件
 *        - 当事件通过eventFilter过滤后被调用
 *        - 用于处理接收到的事件数据
 *        - 可在此更新状态、触发UI变化或执行其他操作
 *
 * @returns {{send: (event: TSend) => void}} 包含通信方法的对象
 * @returns {Function} send 发送事件到服务端的函数
 */
export function useRemoteChannel<TSend = any, TReceive = any>(
  options: {
    /**
     * 事件过滤器函数，决定哪些事件需要被处理
     * @param event 从服务端接收的事件
     * @returns 是否处理该事件
     */
    eventFilter?: (event: TReceive) => boolean;
    /**
     * 事件接收回调函数，处理从服务端接收的事件
     * @param event 从服务端接收的事件
     */
    onReceive?: (event: TReceive) => void;
  } = {}
): { send: (event: TSend) => void } {
  const { eventFilter, onReceive } = options;

  // 使用ref存储回调函数，避免依赖变化导致重复设置监听器
  const callbackRef = useRef(onReceive);
  const filterRef = useRef(eventFilter);

  // 更新ref中存储的回调和过滤函数
  useEffect(() => {
    callbackRef.current = onReceive;
    filterRef.current = eventFilter;
  }, [onReceive, eventFilter]);

  // 设置服务端事件监听器
  useEffect(() => {
    if (!remoteChannel) {
      console.warn("远程通道在当前环境中不可用");
      return;
    }

    // 创建事件处理函数
    const handleClientEvent = (event: TReceive) => {
      // 应用事件过滤器（如果有）
      if (filterRef.current && !filterRef.current(event)) {
        return;
      }

      // 调用接收回调（如果有）
      if (callbackRef.current) {
        try {
          callbackRef.current(event);
        } catch (error) {
          console.error("远程通道事件处理器发生错误:", error);
        }
      }
    };

    // 设置事件监听器
    remoteChannel.onClientEvent(handleClientEvent);

    // 返回清理函数，组件卸载时自动移除事件监听
    return () => {
      // 注意：由于API限制，无法直接移除特定的监听器
      // 在实际应用中，可能需要通过自定义事件系统实现精确的监听器移除
    };
  }, []);

  // 提供发送事件到服务端的函数
  const send = useCallback((event: TSend) => {
    if (!remoteChannel) {
      console.warn("远程通道在当前环境中不可用");
      return;
    }

    try {
      remoteChannel.sendServerEvent(event);
    } catch (error) {
      console.error("向服务端发送事件时发生错误:", error);
    }
  }, []);

  // 返回通信接口
  return { send };
}

/**
 * 导出所有钩子函数
 */
export default {
  useState,
  useEffect,
  useRef,
  useMemo,
  useCallback,
  useScreenSize,
  useContext,
  useReducer,
  useLayoutEffect,
  useDeferredValue,
  useTransition,
  useImperativeHandle,
  useRemoteChannel,
};
